﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Kira.Compiler.Lexer
{
    public class Lexer
    {
        public static int Line { get; set; }

        private Char _peek = ' ';
        private Dictionary<String, Word> _words = new Dictionary<string, Word>();

        private void addWord(Word w)
        {
            this._words.Add(w.Lexeme, w);
        }

        public Lexer()
        {
            this.addWord(new Word("if", Tag.If));
            this.addWord(new Word("else", Tag.Else));
            this.addWord(new Word("while", Tag.While));
            this.addWord(new Word("do", Tag.Do));
            this.addWord(new Word("break", Tag.Break));
            this.addWord(Word.True);
            this.addWord(Word.False);
            this.addWord(Type.Int);
            this.addWord(Type.Char);
            this.addWord(Type.Bool);
            this.addWord(Type.Float);
        }

        private void readChar()
        {
            _peek = (Char)Console.Read();
        }

        private Boolean readChar(char c)
        {
            readChar();
            if (_peek != c) return false;
            _peek = ' ';
            return true;
        }

        public Token Scan()
        {
            for (; ; readChar())
            {
                if (_peek == ' ' || _peek == '\t')
                {
                    continue;
                }
                else if (_peek == '\n')
                {
                    Line++;
                }
                else
                {
                    break;
                }
            }
            switch (_peek)
            {
                case '&':
                    {
                        if (readChar('&'))
                        {
                            return Word.And;
                        }
                        else
                        {
                            return new Token((Tag)'&');
                        }
                    }
                case '|':
                    {
                        if (readChar('|'))
                        {
                            return Word.Or;
                        }
                        else
                        {
                            return new Token((Tag)'|');
                        }
                    }
                case '=':
                    {
                        if (readChar('='))
                        {
                            return Word.Equal;
                        }
                        else
                        {
                            return new Token((Tag)'=');
                        }
                    }
                case '!':
                    {
                        if (readChar('='))
                        {
                            return Word.NotEqual;
                        }
                        else
                        {
                            return new Token((Tag)'!');
                        }
                    }
                case '<':
                    {
                        if (readChar('='))
                        {
                            return Word.Less;
                        }
                        else
                        {
                            return new Token((Tag)'<');
                        }
                    }
                case '>':
                    {
                        if (readChar('='))
                        {
                            return Word.Greater;
                        }
                        else
                        {
                            return new Token((Tag)'>');
                        }
                    }
            }
            //parse digit
            if (Char.IsDigit(_peek))
            {
                int v = 0;
                do
                {
                    v = 10 * v + (int)_peek;
                    readChar();
                }
                while (Char.IsDigit(_peek));
                if (_peek != '.')
                {
                    return new Num(v);
                }
                float x = v;
                float d = 10;
                while (true)
                {
                    readChar();
                    if (!Char.IsDigit(_peek))
                    {
                        break;
                    }
                    x += ((int)_peek) / d;
                    d *= 10;
                }
                return new Real(x);
            }
            if (Char.IsLetter(_peek))
            {
                StringBuilder sb = new StringBuilder();
                do
                {
                    sb.Append(_peek);
                    readChar();
                }
                while (Char.IsLetterOrDigit(_peek));
                String s = sb.ToString();
                if (_words.ContainsKey(s))
                {
                    return _words[s];
                }
                else
                {
                    return new Word(s, Tag.Identifier);
                }
            }
            Token tok = new Token((Tag)_peek);
            _peek = ' ';
            return tok;
        }
    }
}
